package my.soft.aiteacher.teacher.service.Impl;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import my.soft.aiteacher.common.mapper.UserMapper;
import my.soft.aiteacher.common.mapper.VideoInformationMapper;
import my.soft.aiteacher.common.pojo.UserDo;
import my.soft.aiteacher.common.pojo.VideoInformationDo;
import my.soft.aiteacher.exception.BusinessException;
import my.soft.aiteacher.teacher.mapper.*;
import my.soft.aiteacher.teacher.pojo.*;
import my.soft.aiteacher.teacher.service.IFileService;
import my.soft.aiteacher.utils.RedisIdUtils;
import my.soft.aiteacher.utils.SignatureGenerator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class FileServiceImpl implements IFileService {
    @Value("${file.upload-dir}")
    private String uploadPath;

    @Resource
    private FileChunkMapper fileChunkMapper;
    @Resource
    private FileUploadMapper fileUploadMapper;
    @Resource
    private RedisIdUtils redisIdUtils;
    @Resource
    private UserMapper userMapper;
    @Resource
    private VideoCaptionMapper videoCaptionMapper;
    @Resource
    private RedisTemplate<String, List<VideoCaption>> redisTemplate;
    @Resource
    private VideoInformationMapper videoInformationMapper;

    /**
     * 接口--上传文件分片
     *
     * @param file
     * @param chunkIndex
     * @param fileId
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public FileUpload uploadChunk(MultipartFile file, Integer chunkIndex, Long fileId, Long userId) {
        //判断文件是否存在
        if (Objects.isNull(file)) {
            throw new BusinessException("上传分片为空");
        }
        //判断是否能找到文件信息
        FileUpload fileUpload = fileUploadMapper.selectById(fileId);
        if (Objects.isNull(fileUpload)) {
            throw new BusinessException("总文件信息不存在");
        }
        //判断是否存在分片信息

        List<FileChunk> objs = fileChunkMapper.selectList(new QueryWrapper<FileChunk>().eq("file_id", fileId));
        if (!objs.isEmpty() && Objects.equals(objs.size(), fileUpload.getChunkCount())) {
            return fileUpload;
        }

        long chunkSize = file.getSize();
        String chunkMd5 = getMD5(file);
        String originalFilename = file.getOriginalFilename();
        log.info("上传的文件名:{}", originalFilename);
        if (originalFilename == null || !originalFilename.contains(".")) {
            log.error("文件名不合法");
            throw new BusinessException("文件名不合法，缺少扩展名");
        }
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
        String chunkFilename = originalFilename.replace(fileExtension, "") + "_" + chunkIndex + fileExtension;
        UserDo userDo = userMapper.selectById(userId);
        String userVideoDir = uploadPath + userDo.getUsername() + "/" + file.getOriginalFilename().replace(fileExtension, "") + "/";
        File dir = new File(userVideoDir);
        if (!dir.exists()) {
            boolean mkdirs = dir.mkdirs();
            if (!mkdirs) {
                throw new BusinessException("创建文件目录失败：" + userVideoDir);
            }
        }
        try {
            String chunkFilePath = userVideoDir + chunkFilename;
            file.transferTo(new File(chunkFilePath));

            FileChunk fileChunk = FileChunk.builder()
                    .fileId(fileId)
                    .chunkIndex(chunkIndex)
                    .chunkMd5(chunkMd5)
                    .chunkName(chunkFilename)
                    .chunkSize(chunkSize)
                    .id(redisIdUtils.getId("fileChunk"))
                    .createTime(LocalDateTime.now())
                    .chunkPath(chunkFilePath)
                    .build();
            fileChunkMapper.insert(fileChunk);
            if (Objects.equals(chunkIndex + 1, fileUpload.getChunkCount())) {
                File mergeFile = mergeFile(fileUpload);
                String mergeFilePath = mergeFile.getAbsolutePath();
                log.info("开始转换音频...");
                String s = convertVideoToAudio(mergeFile, userVideoDir);
                log.info("音频转换完成，路径: {}", s);
                fileUpload.setAudioPath(s);
                //ffmpeg截取第一帧作为封面
                log.info("开始截取封面...");
                String cover = getCover(mergeFile, userVideoDir);
                log.info("封面截取完成，路径: {}", cover);
                fileUpload.setCoverPath(cover);
                fileUpload.setVideoPath(mergeFilePath);
                log.info("fileUpload:{}", fileUpload);
                fileUploadMapper.update(new UpdateWrapper<FileUpload>().set("video_path", mergeFilePath).set("audio_path", s).set("cover_path", cover).eq("id", fileId));
                //合并完后生成视频信息
                VideoInformationDo videoInformationDo = VideoInformationDo.builder()
                        .fileId(fileId)
                        .userId(userDo.getId())
                        .title(originalFilename.replace(fileExtension, ""))
                        .videoUrl(userVideoDir + originalFilename)
                        .build();
                videoInformationMapper.insert(videoInformationDo);
                return fileUpload;
            }
        } catch (Exception e) {
            log.error("文件上传失败:{}", e.getMessage());
            if (dir.exists()) {
                try {
                    FileUtil.del(dir);
                    fileChunkMapper.delete(new QueryWrapper<FileChunk>().eq("file_id", fileId));
                    fileUploadMapper.deleteById(fileId);
                    throw new BusinessException("文件上传失败已删除服务器中创建的文件——————————" + e.getMessage());
                } catch (Exception ex) {
                    throw new BusinessException("删除文件失败请联系管理员——————————" + e.getMessage());
                }
            }
        }
        return null;
    }

    private String getCover(File mergeFile, String userVideoDir) {
        Process process = null;
        String coverPath = userVideoDir + "cover.jpg";

        File file = new File(coverPath);
        if (file.exists()) {
            log.info("封面已存在，直接返回路径: {}", coverPath);
            return coverPath;
        }

        try {
            String[] command = {
                    "/usr/bin/ffmpeg",
                    "-i", mergeFile.getAbsolutePath(),
                    "-ss", "00:00:01",
                    "-vframes", "1",
                    coverPath
            };
            process = Runtime.getRuntime().exec(command);
            int exitCode = process.waitFor();
            log.info("截取封面成功：{}",command);
            if (exitCode != 0) {
                throw new BusinessException("截取封面失败");
            }
        } catch (Exception e) {
            throw new BusinessException("截取封面失败:" + e.getMessage());
        }finally {
            if (process != null) {
                process.destroy();
            }
        }
        return coverPath;
    }

    /**
     * 接口--上传文件信息返回文件id
     *
     * @param fileUpload
     * @return
     */
    @Override
    public String uploadFile(FileUpload fileUpload) {

        FileUpload obj = fileUploadMapper.selectOne(new QueryWrapper<FileUpload>().eq("file_md5", fileUpload.getFileMd5()).eq("user_id", fileUpload.getUserId()));
        if (Objects.nonNull(obj)) {
            return String.valueOf(obj.getId());
        }
        Long fileId = redisIdUtils.getId("file");
        fileUpload.setId(fileId);
        fileUploadMapper.insert(fileUpload);
        return String.valueOf(fileId);
    }

    /**
     * 接口--上传字幕
     *
     * @param videoCaptionList
     * @return
     */
    @Override
    public String uploadCaption(List<VideoCaption> videoCaptionList) {
        StringBuilder caption = new StringBuilder();
        Long fileId = videoCaptionList.get(0).getFileId();
        List<VideoCaption> videoCaptions = videoCaptionMapper.selectList(new QueryWrapper<VideoCaption>().eq("file_id", fileId));
        if(!videoCaptions.isEmpty()){
            for (VideoCaption v : videoCaptions) {
                caption.append(v.getCaption());
            }
            return caption.toString();
        }
        for (VideoCaption v : videoCaptionList) {
            caption.append(v.getCaption());
            videoCaptionMapper.insert(v);
        }
        return caption.toString();
    }

    /**
     * 接口--获取字幕
     *
     * @param fileId
     * @return
     */
    @Override
    public List<VideoCaption> getCaption(Long fileId) {
        return videoCaptionMapper.selectList(new QueryWrapper<VideoCaption>().eq("file_id", fileId).orderByAsc("sort"));
    }

    /**
     * 更新字幕
     *
     * @param videoCaptionList
     * @return
     */
    @Transactional
    @Override
    public List<VideoCaption> updateCaption(List<VideoCaption> videoCaptionList) {
        for (VideoCaption v : videoCaptionList) {
            int i = videoCaptionMapper.update(v, new QueryWrapper<VideoCaption>().eq("sort", v.getSort()).eq("file_id", v.getFileId()));
            if (i == 0) {
                throw new BusinessException("更新失败");
            }
        }
        List<VideoCaption> videoCaptions = videoCaptionMapper.selectList(new QueryWrapper<VideoCaption>().eq("file_id", videoCaptionList.get(0).getFileId()));
        redisTemplate.opsForValue().set(String.valueOf(videoCaptionList.get(0).getFileId()), videoCaptions);
        return videoCaptions;
    }


    @Override
    public File getVideo(String fileId) {
        if (Objects.isNull(fileId)) {
            throw new BusinessException("文件id不存在");
        }
        FileUpload fileUpload = fileUploadMapper.selectById(fileId);
        if (Objects.isNull(fileUpload)) {
            throw new BusinessException("文件信息不存在");
        }
        return mergeFile(fileUpload);
    }

    @Override
    public List<FileUpload> getTeacherVideoInformation(Long userId) {
        return fileUploadMapper.selectList(new QueryWrapper<FileUpload>().eq("user_id", userId));
    }

    @Override
    @Transactional
    public void deleteVideo(Long userId, Long fileId) {
        FileUpload fileUpload = fileUploadMapper.selectOne(new QueryWrapper<FileUpload>().eq("user_id",userId).eq("id",fileId));
        if(Objects.isNull(fileUpload)){
            throw new BusinessException("当前视频不存在,请联系管理员");
        }
        fileUploadMapper.deleteById(fileId);
        File file = new File(fileUpload.getVideoPath().substring(0,fileUpload.getVideoPath().lastIndexOf("/")));
        log.info("删除文件路径:{}",fileUpload.getVideoPath().substring(0,fileUpload.getVideoPath().lastIndexOf("/")));
        //删除文件夹
        if(file.exists()){
            try{
                FileUtil.del(file);
                log.info("文件夹删除成功: {}", file.getAbsolutePath());
            }catch (Exception e){
                log.error("文件夹删除失败: {}", file.getAbsolutePath(), e);
                throw new BusinessException("文件夹删除失败: " + e.getMessage());
            }
        }
    }

    /**
     * 将视频文件转成音频文件
     *
     * @param videoFile
     * @param userVideoDir
     * @return
     */
    private String convertVideoToAudio(File videoFile, String userVideoDir) {
        //利用liunx控制台调用ffmpeg将videoFile文件转成audio保存到liunx服务器中
        String name = videoFile.getName();
        String audioFileDir = name.replace(name.substring(name.lastIndexOf(".")), ".pcm");
        File audioFile = new File(userVideoDir, audioFileDir);
        // 构建ffmpeg命令
        String[] command = {
                "/usr/bin/ffmpeg",
                "-y", // 覆盖已存在文件
                "-i", videoFile.getAbsolutePath(),
                "-vn", // 禁用视频流
                "-f", "s16le", // PCM格式
                "-acodec", "pcm_s16le", // 16位PCM编码
                "-ar", "16000", // 采样率16kHz
                "-ac", "1", // 单声道
                audioFile.getAbsolutePath()
        };
        Process process = null;
        try {
            // 执行命令
             process = Runtime.getRuntime().exec(command);
            process.waitFor();

            return audioFile.getAbsolutePath();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("视频转音频失败：" + e.getMessage());
        }finally {
            if (process!=null){
                process.destroy();
            }
        }
    }


    public static String getMD5(MultipartFile file) {
        try {
            byte[] uploadBytes = file.getBytes();
            MessageDigest MD5 = MessageDigest.getInstance("MD5");
            byte[] digest = MD5.digest(uploadBytes);
            String string = new BigInteger(1, digest).toString(16);
            return string;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getMD5(File file) {
        try {
            MessageDigest MD5 = MessageDigest.getInstance("MD5");
            byte[] digest = MD5.digest(FileUtil.readBytes(file));
            String string = new BigInteger(1, digest).toString(16);
            return string;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("获取文件MD5值失败");
        }
    }

    /**
     * 合并分片文件
     *
     * @param fileUpload
     * @return
     */
    private File mergeFile(FileUpload fileUpload) {
        UserDo userDo = userMapper.selectById(fileUpload.getUserId());
        if (Objects.isNull(userDo)) {
            throw new BusinessException("用户信息不存在");
        }
        List<FileChunk> fileChunkList = fileChunkMapper.selectList(
                new QueryWrapper<FileChunk>()
                        .eq("file_id", fileUpload.getId())
                        .orderByAsc("chunk_index"));

        if (fileChunkList.isEmpty()) {
            throw new BusinessException("分片信息不存在");
        }
        //合并文件并保存到服务器中
        try {
            //去除后缀
            String fileName = fileUpload.getFileName().substring(0, fileUpload.getFileName().lastIndexOf("."));
            String dirPath = uploadPath + "/" + userDo.getUsername() + "/" + fileName + "/";
            File mergeFile = new File(dirPath,
                    fileUpload.getFileName());
            try (FileChannel outChannel = new FileOutputStream(mergeFile).getChannel()) {
                for (FileChunk fileChunk : fileChunkList) {
                    File chunkFile = new File(fileChunk.getChunkPath());
                    try (FileChannel inChannel = new FileInputStream(chunkFile).getChannel()) {
                        inChannel.transferTo(0, inChannel.size(), outChannel);
                    }
                }
            }
            return mergeFile;

        } catch (Exception e) {
            throw new BusinessException("文件处理失败:" + e.getMessage());
        }
    }

//    /**
//     * 合并分片文件
//     *
//     * @param fileUpload
//     * @return
//     */
//    private File mergeFileTOHls(FileUpload fileUpload) {
//        UserDo userDo = userMapper.selectById(fileUpload.getUserId());
//        if (Objects.isNull(userDo)) {
//            throw new BusinessException("用户信息不存在");
//        }
//        List<FileChunk> fileChunkList = fileChunkMapper.selectList(
//                new QueryWrapper<FileChunk>()
//                        .eq("file_id", fileUpload.getId())
//                        .orderByAsc("chunk_index"));
//
//        if (fileChunkList.isEmpty()) {
//            throw new BusinessException("分片信息不存在");
//        }
//        try {
//            String fileName = fileUpload.getFileName().substring(fileUpload.getFileName().lastIndexOf("."));
//            File mergeFile = File.createTempFile("merged-",
//                    fileName);
//
//            try (FileChannel outChannel = new FileOutputStream(mergeFile).getChannel()) {
//                for (FileChunk fileChunk : fileChunkList) {
//                    File chunkFile = new File(fileChunk.getChunkPath());
//                    try (FileChannel inChannel = new FileInputStream(chunkFile).getChannel()) {
//                        inChannel.transferTo(0, inChannel.size(), outChannel);
//                    }
//                }
//            }
//            String hlsDirPath = uploadPath + "/" + userDo.getUsername() + "/" + fileName + "/hls_" + fileName + "/";
//            File hlsDir = new File(hlsDirPath);
//            if (!hlsDir.exists()) {
//                hlsDir.mkdirs();
//            }
//
//            String[] ffmpegCommand = {
//                    "ffmpeg",
//                    "-i", mergeFile.getAbsolutePath(),
//                    "-codec:", "copy",
//                    "-start_number", "0",
//                    "-hls_time", "10",
//                    "-hls_list_size", "0",
//                    "-f", "hls",
//                    hlsDirPath + mergeFile.getName() + ".m3u8"
//            };
//
//            Process process = Runtime.getRuntime().exec(ffmpegCommand);
//            int exitCode = process.waitFor();
//            if (exitCode != 0) {
//                throw new BusinessException("HLS转换失败，退出码: " + exitCode);
//            }
//            // 返回HLS主文件
//            return new File(hlsDirPath + "output.m3u8");
//
//        } catch (Exception e) {
//            throw new BusinessException("文件处理失败:" + e.getMessage());
//        }
//    }

    /**
     * 上传音频到讯飞（用不上了）
     *
     * @param audioPath
     * @return
     */
    private HttpResponse<String> XFUploadAudio(String audioPath) {
        String fileName = audioPath.substring(audioPath.lastIndexOf("/") + 1);
        String signa = SignatureGenerator.generateSigna();
        String url = "https://raasr.xfyun.cn/v2/api/upload?" + "fileName=" + fileName + "&signa=" + signa + "&duration=200" + "&fileSize=12345" + "&appId=68d2eee6" + "&audioMode=urlLink" + "&audioUrl=" + "http://81.68.128.249:9002" + audioPath.replace("/home/uploads", "");

        URI uri = URI.create(url);
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(uri)
                .POST(HttpRequest.BodyPublishers.noBody())
                .build();
        try {
            HttpResponse<String> send = client.send(request, HttpResponse.BodyHandlers.ofString());
            return send;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("请求出错: " + e.getMessage());
        }
    }
}
